Adding elements

Elizabeth King
Kevin Middleton

Data-to-ink ratio

Don’t add complexity to your plot without a good reason.

Some journals don’t like text in plots.

Annotating plots

Adding elements to plots

  • Lines
  • Boxes, shading plot areas
  • Images (jpg, png)
  • Maps
  • Text and math text

annotate()

  • Adds elements without using geom_
  • Doesn’t require data.frames or tibbles
  • Useful for single elements

Boxes and shading

  • Time series (day/night, seasons, years)
  • Differentiate chromosomes, etc.

geom_rect() uses values for four corners: xmin, xmax, ymin, and ymax

Mouse wheel activity

WR <- read_csv("Data/Mouse_wheel.csv", show_col_types = FALSE) |> 
  mutate(Date = mdy_hm(Date) - hours(1)) |> 
  pivot_longer(cols = -Date, names_to = "Wheel", values_to = "Revolutions") |> 
  mutate(Wheel = str_remove(Wheel, "Wheel") |> as.integer()) |> 
  filter(Wheel <= 4)

WR
# A tibble: 11,516 × 3
   Date                Wheel Revolutions
   <dttm>              <int>       <dbl>
 1 2013-05-07 11:01:00     1           0
 2 2013-05-07 11:01:00     2           0
 3 2013-05-07 11:01:00     3           0
 4 2013-05-07 11:01:00     4           0
 5 2013-05-07 11:02:00     1           0
 6 2013-05-07 11:02:00     2           0
 7 2013-05-07 11:02:00     3           0
 8 2013-05-07 11:02:00     4           0
 9 2013-05-07 11:03:00     1           0
10 2013-05-07 11:03:00     2           0
# … with 11,506 more rows

Create a tibble with x bounds of the box

DayNight <- tibble(
  Lights_off = ymd_hms(c("2013-05-07 19:00:00", "2013-05-08 19:00:00")),
  Lights_on = ymd_hms(c("2013-05-08 07:00:00", "2013-05-09 07:00:00"))
)
# A tibble: 2 × 2
  Lights_off          Lights_on          
  <dttm>              <dttm>             
1 2013-05-07 19:00:00 2013-05-08 07:00:00
2 2013-05-08 19:00:00 2013-05-09 07:00:00

Plot with background geom_rect()

  • Inf and -Inf used when you don’t know the limits
ggplot() +
  geom_rect(data = DayNight,
            aes(xmin = Lights_off, xmax = Lights_on,
                ymin = 0, ymax = Inf),
            fill = "gray80") +
  geom_path(data = WR, aes(x = Date, y = Revolutions, group = Wheel)) +
  facet_wrap("Wheel")

Plot with background geom_rect()

Refine plot

  • Change date labels, rotate text
  • Change background of facet headers
ggplot() +
  geom_rect(data = DayNight,
            aes(xmin = Lights_on, xmax = Lights_off,
                ymin = 0, ymax = Inf),
            fill = "gray80") +
  geom_path(data = WR, aes(x = Date, y = Revolutions, group = Wheel)) +
  facet_wrap("Wheel") +
  scale_x_datetime(date_labels = "%H:%M",
                   name = "Time") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5),
        strip.background = element_rect(fill = "goldenrod"))

Refine plot

Overlaying data on Images

  • Loading image
  • Setting up coordinates with tribble()
img <- png::readPNG("Images/Craniometric_tracing.png")

coords <- tibble::tribble(
  ~ LM, ~ x, ~ y,
  "ANS", 318, 249,
  "Articulare", 44, 193,
  "Basion", 7, 221,
  "Condylion", 59, 169,
  "Gonion", 73.5, 332,
  "Menton", 215, 415,
  "Nasion", 313, 96,
  "PNS", 153, 241,
  "Pogonion", 239, 424,
  "Sella", 79, 116
) |> 
  mutate(y = dim(img)[1] - y) # Reverse due to R/ImageJ differences

Images are matrices (grayscale) or arrays (color)

class(img)
[1] "matrix" "array" 
str(img)
 num [1:463, 1:400] 1 1 1 1 1 1 1 1 1 1 ...
img[1:10, 1:10]
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    1    1    1    1    1    1    1    1    1     1
 [2,]    1    1    1    1    1    1    1    1    1     1
 [3,]    1    1    1    1    1    1    1    1    1     1
 [4,]    1    1    1    1    1    1    1    1    1     1
 [5,]    1    1    1    1    1    1    1    1    1     1
 [6,]    1    1    1    1    1    1    1    1    1     1
 [7,]    1    1    1    1    1    1    1    1    1     1
 [8,]    1    1    1    1    1    1    1    1    1     1
 [9,]    1    1    1    1    1    1    1    1    1     1
[10,]    1    1    1    1    1    1    1    1    1     1

plot with annotation_raster()

  • xmin, xmax, ymin, and ymax define the box for the image
  • dim(img) gives the dimensions of the matrix
  • Use ggpubr::theme_transparent() to remove all plot axes, labels, etc.
ggplot(data = coords, aes(x, y)) +
    annotation_raster(img,
                      xmin = 0, xmax = dim(img)[2],
                      ymin = 0, ymax = dim(img)[1]) +
    geom_point(color = "red", size = 5) +
    xlim(c(0, dim(img)[2])) +
    ylim(c(0, dim(img)[1])) +
    coord_equal() +
    ggpubr::theme_transparent()

plot with annotation_raster()

Maps

Field sites along the Missouri River

library(ggmap)

Locations <- 
  tribble(~ lat,     ~lon,       ~ ID,
          38.666710, -91.867078, "Mokane",
          38.587094, -92.176563, "Jefferson City",
          38.688121, -92.363351, "Marion",
          38.799781, -92.378246, "Easley",
          38.974020, -92.565014, "Rocheport",
          38.980450, -92.740881, "Boonville",
          39.064000, -92.935129, "Arrow Rock",
          39.232537, -92.848955, "Glasgow")

Setup and plot map with data

# Create bounding box for locations
sbbox <- make_bbox(lon = Locations$lon,
                   lat = Locations$lat,
                   f = 0.25) # Expansion factor

# Download map
sq_map <- get_stamenmap(bbox = sbbox,
                        maptype = "terrain")

p <- ggmap(sq_map) + 
  geom_point(data = Locations,
             mapping = aes(x = lon, y = lat),
             color = "red",
             size = 3) +
  labs(x = "Longitude", y = "Latitude")
p

Setup and plot map with data

Text annotations

p +
  annotate(geom = "text",
           x = -92.45,
           y = 39.35,
           label = "Field Sites on the Missouri River",
           size = 8)

Text annotations

Labeling points with ggrepel package

library(ggrepel)

p +
  annotate(geom = "text",
           x = -92.45,
           y = 39.35,
           label = "Field Sites on the Missouri River",
           size = 8) +
  geom_text_repel(data = Locations,
                  mapping = aes(x = lon, y = lat, label = ID),
                  box.padding = 1,
                  seed = 431579)

Labeling points with ggrepel package

Working with math

Several options

  • ?plotmath which uses expression() and paste()
  • annotate() with parse = TRUE
  • latex2exp package converts LaTeX expressions to plotmath

Regression data

# A tibble: 20 × 2
       x     y
   <dbl> <dbl>
 1 5.53  21.3 
 2 7.53  32.1 
 3 7.26  33.0 
 4 3.52  14.0 
 5 0.111  2.24
 6 3.55  14.1 
 7 2.18  10.3 
 8 6.14  27.9 
 9 8.58  35.6 
10 7.38  30.3 
11 1.45   8.86
12 9.52  39.7 
13 5.14  21.0 
14 1.38   6.19
15 6.24  23.1 
16 1.34   4.11
17 0.547  6.15
18 3.66  15.9 
19 6.87  28.4 
20 6.02  23.6 

Modifying axis titles

Note:

  • ~ for intentional space, other spaces are collapsed
  • No "
ggplot(M, aes(x, y)) +
  geom_point() +
  labs(x = expression(Area~(sqrt(m^2))),
       y = expression(Species~density~(Species / m^2)))

Modifying axis titles

Adding an annotation

fm <- lm(y ~ x, data = M)
s <- summary(fm)

ggplot(M, aes(x, y)) +
  geom_point() +
  labs(x = expression(Area~(sqrt(m^2))),
       y = expression(Species~density~(Species / m^2))) +
  annotate(geom = "text",
           label = paste("italic(R) ^ 2 == ",
                         round(s$r.squared, 2)),
           parse = TRUE,
           x = 7.5, y = 10,
           size = 5)

Adding an annotation

Formatting text with Tex()

  • Uses raw strings: TeX(r'($ code goes here $)')
  • LaTeX math
  • Works a little differently in annotate()
library(latex2exp)

ggplot(M, aes(x, y)) +
  geom_point() +
  labs(x = TeX(r'($ \sqrt{\frac{1}{Area}} \cdot \cos (2\pi) + \theta_{1}$)'),
       y = TeX(r'(Species density$\,\,\left( \frac{Spp.}{m^2} \right) $)')) +
  annotate(geom = "text",
           label = TeX(paste('$\\textit{R}^2 =$', round(s$r.squared, 2)),
                       output = "character"),
           parse = TRUE,
           x = 7.5, y = 10,
           size = 5)

Formatting text with Tex()